package com.tsfyun.scm.service.impl.finance;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Snowflake;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.tsfyun.common.base.dto.TaskDTO;
import com.tsfyun.common.base.enums.BusinessTypeEnum;
import com.tsfyun.common.base.enums.DistributedLockEnum;
import com.tsfyun.common.base.enums.SerialNumberTypeEnum;
import com.tsfyun.common.base.enums.domain.DomainOprationEnum;
import com.tsfyun.common.base.enums.domain.DomainTypeEnum;
import com.tsfyun.common.base.enums.domain.InvoiceStatusEnum;
import com.tsfyun.common.base.enums.finance.InvoicePointEnum;
import com.tsfyun.common.base.enums.finance.InvoiceTaxTypeEnum;
import com.tsfyun.common.base.enums.finance.InvoiceTypeEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.security.SecurityUtil;
import com.tsfyun.common.base.support.DomainStatus;
import com.tsfyun.common.base.util.ClassUtil;
import com.tsfyun.common.base.util.DateUtils;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.scm.dto.file.ExcelKeyValue;
import com.tsfyun.scm.dto.file.ExportExcelParamsDTO;
import com.tsfyun.scm.dto.finance.ConfirmTaxCodeDTO;
import com.tsfyun.scm.dto.finance.InvoiceCompleteDTO;
import com.tsfyun.scm.dto.finance.InvoiceQTO;
import com.tsfyun.scm.dto.finance.client.ClientConfirmInvoiceDTO;
import com.tsfyun.scm.dto.finance.client.ClientInvoiceQTO;
import com.tsfyun.scm.dto.support.TaskNoticeContentDTO;
import com.tsfyun.scm.entity.customer.Agreement;
import com.tsfyun.scm.entity.customer.Customer;
import com.tsfyun.scm.entity.finance.*;
import com.tsfyun.scm.entity.order.ImpOrder;
import com.tsfyun.scm.entity.order.ImpOrderCost;
import com.tsfyun.scm.mapper.finance.InvoiceMapper;
import com.tsfyun.scm.service.common.ICommonService;
import com.tsfyun.scm.service.customer.IAgreementService;
import com.tsfyun.scm.service.file.IExportFileService;
import com.tsfyun.scm.service.finance.*;
import com.tsfyun.common.base.extension.ServiceImpl;
import com.tsfyun.scm.service.order.IImpOrderCostService;
import com.tsfyun.scm.service.order.IImpOrderInvoiceService;
import com.tsfyun.scm.service.order.IImpOrderService;
import com.tsfyun.scm.service.support.ITaskNoticeContentService;
import com.tsfyun.scm.service.system.ISerialNumberService;
import com.tsfyun.scm.service.system.IStatusHistoryService;
import com.tsfyun.scm.service.system.ISubjectBankService;
import com.tsfyun.scm.service.system.ISubjectService;
import com.tsfyun.scm.util.ClientInfoStatus;
import com.tsfyun.scm.util.ClientStatusMappingUtil;
import com.tsfyun.scm.vo.finance.*;
import com.tsfyun.scm.vo.finance.client.ClientInvoiceListVO;
import com.tsfyun.scm.vo.order.ImpOrderInvoiceVO;
import com.tsfyun.scm.vo.system.StatusHistoryVO;
import com.tsfyun.scm.vo.system.SubjectBankVO;
import com.tsfyun.scm.vo.system.SubjectSimpleVO;
import com.tsfyun.scm.vo.system.SubjectVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * <p>
 * 发票 服务实现类
 * </p>
 *

 * @since 2020-05-15
 */
@RefreshScope
@Service
@Slf4j
public class InvoiceServiceImpl extends ServiceImpl<Invoice> implements IInvoiceService {

    @Autowired
    private InvoiceMapper invoiceMapper;
    @Autowired
    private ISubjectService subjectService;
    @Autowired
    private ISubjectBankService subjectBankService;
    @Autowired
    private IInvoiceCostMemberService invoiceCostMemberService;
    @Autowired
    private ISerialNumberService serialNumberService;
    @Autowired
    private IStatusHistoryService statusHistoryService;
    @Autowired
    private ITaskNoticeContentService taskNoticeContentService;
    @Autowired
    private IInvoiceMemberService invoiceMemberService;
    @Autowired
    private IImpOrderService impOrderService;
    @Autowired
    private IImpSalesContractMemberService impSalesContractMemberService;
    @Autowired
    private IImpSalesContractService impSalesContractService;
    @Autowired
    private ICommonService commonService;
    @Autowired
    private RedisLockRegistry redisLockRegistry;
    @Value("${redis.lock.time:5}")
    private Integer lockTime;
    @Autowired
    private Snowflake snowflake;
    @Autowired
    private IImpOrderCostService impOrderCostService;
    @Autowired
    private IImpOrderInvoiceService impOrderInvoiceService;
    @Autowired
    private ITaxCodeNameService taxCodeNameService;
    @Autowired
    private IAgreementService agreementService;
    @Autowired
    private IExportFileService exportService;

    @Override
    public PageInfo<InvoiceVO> pageList(InvoiceQTO qto) {
        //如果传了动态日期字段，检查是否包含该字段以防数据库报错
        if(StringUtils.isNotEmpty(qto.getQueryDate())) {
            TsfPreconditions.checkArgument(ClassUtil.containsField(Invoice.class,qto.getQueryDate()),new ServiceException("日期查询类型参数错误"));
        }
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        Map<String,Object> params = beanMapper.map(qto, Map.class);
        List<InvoiceVO> list = invoiceMapper.list(params);
        return new PageInfo<>(list);
    }

    @Override
    public Long exportSpaceFlightExcel(List<Long> ids) throws Exception{
        List<InvoiceMemberExcel> invoiceMembers = invoiceMemberService.findByInvoiceIds(ids);
        ExportExcelParamsDTO params = new ExportExcelParamsDTO();
        params.setFileName(String.format("开票资料%s", DateUtils.fromatShortNoSign(new Date())));
        params.setSheetName("开票资料");
        params.setOperator(SecurityUtil.getCurrentPersonIdAndName());
        params.setDatas(invoiceMembers);
        List<ExcelKeyValue> files = new ArrayList<ExcelKeyValue>(){{
            add(new ExcelKeyValue("docNo","单据编号"));
            add(new ExcelKeyValue("orderNo","订单编号"));
            add(new ExcelKeyValue("invoiceName","商品及劳务名称"));
            add(new ExcelKeyValue("buyerName","发票抬头/客户信息（名称）"));
            add(new ExcelKeyValue("unitName","计量单位"));
            add(new ExcelKeyValue("goodsModel","规格型号"));
            add(new ExcelKeyValue("quantity","数量"));
            add(new ExcelKeyValue("unitPrice","单价（含税/不含税)"));
            add(new ExcelKeyValue("totalPrice","金额（含税/不含税)"));
            add(new ExcelKeyValue("memo","备注"));
            add(new ExcelKeyValue("version","编码版本号"));
            add(new ExcelKeyValue("taxCodeName","税收分类名称"));
            add(new ExcelKeyValue("taxCode","税收分类编码"));
            add(new ExcelKeyValue("favouredPolicy","享受优惠政策"));
            add(new ExcelKeyValue("memo","优惠政策内容"));
            add(new ExcelKeyValue("memo","零税率标识"));
            add(new ExcelKeyValue("memo","企业自编码"));
            add(new ExcelKeyValue("memo","扣除额"));

        }};
        params.setFiles(files);
        return exportService.exportExcel(params);
    }

    @Override
    public Invoice getByOrderNo(String orderNo) {
        Invoice condition = new Invoice();
        condition.setOrderNo(orderNo);
        return super.getOne(condition);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void generateInvoice(ImpOrderInvoiceVO impOrderInvoiceVO,List<ImpOrderCost> orderCosts) {
        if(CollUtil.isEmpty(orderCosts)) {return; }
        BigDecimal invoiceVal = StringUtils.rounded2(orderCosts.stream().map(ImpOrderCost::getReceAmount).reduce(BigDecimal.ZERO,BigDecimal::add));
        //取企业信息和企业开票银行信息（默认的人民币）
        SubjectVO subject = subjectService.findByCode();
        List<SubjectBankVO> subjectBankVOS = subjectBankService.selectEnableBanks();
        TsfPreconditions.checkArgument(CollUtil.isNotEmpty(subjectBankVOS),new ServiceException("请联系管理员配置企业银行信息"));
        SubjectBankVO subjectBankVO = subjectBankVOS.stream().filter(r->Objects.equals("CNY",r.getCurrencyId())
                && Objects.equals(Boolean.TRUE,r.getIsDefault())).findFirst().orElseThrow(()->new ServiceException("请联系管理员配置默认的人民币企业银行信息"));

        //赋值保存发票信息
        Invoice invoice = new Invoice();
        invoice.setDocNo(serialNumberService.generateDocNo(SerialNumberTypeEnum.INVOICE));
        invoice.setOrderId(impOrderInvoiceVO.getId());
        invoice.setOrderNo(impOrderInvoiceVO.getDocNo());
        InvoiceStatusEnum statusEnum = InvoiceStatusEnum.WAIT_CONFIRM;
        invoice.setStatusId(statusEnum.getCode());
        invoice.setTaxType(InvoiceTaxTypeEnum.AGENT.getCode());
        invoice.setApplyDate(LocalDateTime.now());
        invoice.setBuyerId(impOrderInvoiceVO.getCustomerId());
        invoice.setBuyerName(impOrderInvoiceVO.getCustomerName());
        invoice.setBuyerBankName(impOrderInvoiceVO.getInvoiceBankName());
        invoice.setBuyerTaxpayerNo(impOrderInvoiceVO.getTaxpayerNo());
        invoice.setBuyerBankAccount(impOrderInvoiceVO.getInvoiceBankAccount());
        invoice.setBuyerBankTel(impOrderInvoiceVO.getInvoiceBankTel());
        invoice.setBuyerBankAddress(impOrderInvoiceVO.getInvoiceBankAddress());
        invoice.setBuyerLinkPerson(impOrderInvoiceVO.getLinkPerson());
        invoice.setSellerId(subject.getId());
        invoice.setSellerBankName(subjectBankVO.getName());
        invoice.setSellerBankAccount(subjectBankVO.getAccount());
        invoice.setSellerBankAddress(subjectBankVO.getAddress());
        invoice.setInvoiceType(InvoiceTypeEnum.VAT.getCode());
        invoice.setTaxPoint(new BigDecimal(InvoicePointEnum.SIX.getCode()));
        //开票申请金额
        invoice.setInvoiceVal(invoiceVal);
        try {
            super.saveNonNull(invoice);
        } catch (DuplicateKeyException e) {
            throw new ServiceException("生成的发票系统单号已经被占用，请稍后再试");
        }
        //保存发票明细信息
        invoiceMemberService.saveAgentMember(invoice);
        //保存发票费用信息
        ImpOrder impOrder = new ImpOrder();
        impOrder.setId(impOrderInvoiceVO.getId());
        impOrder.setDocNo(impOrderInvoiceVO.getDocNo());
        invoiceCostMemberService.saveAgentMember(invoice,impOrder,orderCosts);
        //修改订单上的发票单号
        impOrderService.setInvoiceNo(impOrder.getId(),null,invoice.getDocNo());
        //记录单据状态变更
        InvoiceStatusEnum invoiceStatusEnum = InvoiceStatusEnum.of(invoice.getStatusId());
        statusHistoryService.saveHistory(DomainOprationEnum.INVOICE_CREATE,
                invoice.getId().toString(),DomainTypeEnum.INVOICE.getCode(),
                invoiceStatusEnum.getCode(),invoiceStatusEnum.getName(),"创建开票申请");
        //发送任务通知
        TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
        taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
        taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
        taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
        if(Objects.equals(invoiceStatusEnum,statusEnum)) {
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_SALE.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】有一单发票申请【%s】需要您确认。", invoice.getBuyerName(), invoice.getDocNo()));
        } else if(Objects.equals(invoiceStatusEnum,InvoiceStatusEnum.WAIT_FINANCE_TAX)) {
            super.updateById(invoice);
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_TAX.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】有一单发票申请【%s】需要您确认税收分类编码。", invoice.getBuyerName(), invoice.getDocNo()));
        }
        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Override
    public InvoiceDetailPlusVO detailPlus(Long id,String operation) {
        Invoice invoice = super.getById(id);
        Optional.ofNullable(invoice).orElseThrow(()->new ServiceException("发票信息不存在"));
        //验证状态是否可以执行操作
        DomainStatus.getInstance().check(DomainOprationEnum.of(operation), invoice.getStatusId());
        //获取发票明细信息
        List<InvoiceMemberVO> members = invoiceMemberService.getMembersByInvoiceId(id);
        //获取发票费用明细信息
        List<InvoiceCostMemberVO> costMembers = invoiceCostMemberService.getMembersByOrderNo(invoice.getOrderNo());
        InvoiceVO invoiceVO = beanMapper.map(invoice,InvoiceVO.class);
        // 商务开票确认
        if(Objects.equals(DomainOprationEnum.INVOICE_CONFIRM_SALE,DomainOprationEnum.of(operation))){
            //查询客户确认意见
            StatusHistoryVO statusHistoryVO  = statusHistoryService.findRecentByDomainAndHistoryStatus(invoice.getId().toString(),Invoice.class.getName(),InvoiceStatusEnum.WAIT_CUSTOMER_TAX.getCode());
            if(Objects.nonNull(statusHistoryVO)){
                invoiceVO.setCustomerReason(statusHistoryVO.getMemo());
            }
        }
        if(Objects.nonNull(invoiceVO.getSellerId())){
            SubjectSimpleVO subjectSimpleVO = subjectService.getSimpleById(invoiceVO.getSellerId());
            invoiceVO.setSellerName(Objects.nonNull(subjectSimpleVO) ? subjectSimpleVO.getName() : null);
        }
        return new InvoiceDetailPlusVO(invoiceVO,members,costMembers);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void confirm(TaskDTO dto) {

    }

    @Override
    public Invoice getBySalesContractNo(String contractNo) {
        Invoice condition = new Invoice();
        condition.setSaleContractNo(contractNo);
        List<Invoice> invoices = super.list(condition);
        if(CollUtil.isNotEmpty(invoices)) {
            return invoices.get(0);
        }
        return null;
    }

    /**
     * 根据销售合同生成发票申请
     * @param impSalesContract
     * @param impSalesContractMembers
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateInvoiceFromSalesContract(ImpSalesContract impSalesContract, List<ImpSalesContractMemberVO> impSalesContractMembers) {
        //赋值保存发票信息
        Invoice invoice = new Invoice();
        invoice.setDocNo(serialNumberService.generateDocNo(SerialNumberTypeEnum.INVOICE));
        invoice.setOrderId(impSalesContract.getImpOrderId());
        invoice.setOrderNo(impSalesContract.getImpOrderNo());
        //默认商务确定开票
        invoice.setStatusId(InvoiceStatusEnum.WAIT_CONFIRM.getCode());
        invoice.setTaxType(InvoiceTaxTypeEnum.PROPRIETARY.getCode());
        invoice.setApplyDate(LocalDateTime.now());
        invoice.setBuyerId(impSalesContract.getBuyerId());
        invoice.setBuyerName(impSalesContract.getBuyerName());
        invoice.setBuyerBankName(impSalesContract.getBuyerBankName());
        invoice.setBuyerTaxpayerNo(impSalesContract.getBuyerTaxpayerNo());
        invoice.setBuyerBankAccount(impSalesContract.getBuyerBankAccount());
        invoice.setBuyerBankTel(impSalesContract.getBuyerBankTel());
        invoice.setBuyerBankAddress(impSalesContract.getBuyerBankAddress());
        invoice.setBuyerLinkPerson(impSalesContract.getBuyerLinkPerson());
        invoice.setSellerId(impSalesContract.getSellerId());
        invoice.setSellerBankName(impSalesContract.getSellerBankName());
        invoice.setSellerBankAccount(impSalesContract.getSellerBankAccount());
        invoice.setSellerBankAddress(impSalesContract.getSellerBankAddress());
        invoice.setInvoiceType(InvoiceTypeEnum.VAT.getCode());
        invoice.setTaxPoint(new BigDecimal(InvoicePointEnum.THIRTEEN.getCode()));
        //开票申请金额
        invoice.setInvoiceVal(impSalesContract.getTotalPrice());
        invoice.setSaleContractNo(impSalesContract.getDocNo());
        invoice.setOrderNo(impSalesContract.getImpOrderNo());
        // 明细中如果存在未匹配到的税收分类编码状态会变成待财务确定编码
        List<InvoiceMember> invoiceMembers = invoiceMemberService.saveMemberBySalesContractMembers(invoice,impSalesContractMembers);
        // 如果不是待财务确定编码 则需要判断是否存在首次开票情况
        if(!Objects.equals(InvoiceStatusEnum.WAIT_FINANCE_TAX,InvoiceStatusEnum.of(invoice.getStatusId()))){
            for(InvoiceMember invoiceMember : invoiceMembers){
                if(invoiceMember.getIsFirst()){
                    invoice.setStatusId(InvoiceStatusEnum.WAIT_CUSTOMER_TAX.getCode());
                    break;
                }
            }
        }
        try {
            //保存发票主单
            super.saveNonNull(invoice);
        } catch (DuplicateKeyException e) {
            throw new ServiceException("生成的发票系统单号已经被占用，请稍后再试");
        }
        invoiceMembers.stream().forEach(invoiceMember -> {
            invoiceMember.setInvoiceId(invoice.getId());
        });
        //批量保存发票明细
        invoiceMemberService.savaBatch(invoiceMembers);
        //修改订单发票单号
        impOrderService.setInvoiceNo(impSalesContract.getImpOrderId(),null,invoice.getDocNo());
        //修改销售合同上的发票号
        impSalesContractService.setInvoiceNo(impSalesContract.getId(),null,invoice.getDocNo());
        //记录单据状态变更
        InvoiceStatusEnum invoiceStatusEnum = InvoiceStatusEnum.of(invoice.getStatusId());
        statusHistoryService.saveHistory(DomainOprationEnum.INVOICE_CREATE,
                invoice.getId().toString(),DomainTypeEnum.INVOICE.getCode(),
                invoiceStatusEnum.getCode(),invoiceStatusEnum.getName(),"创建开票申请");
        //待财务确定编码
        if(Objects.equals(invoiceStatusEnum,InvoiceStatusEnum.WAIT_FINANCE_TAX)){
            //发送任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
            taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
            taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_TAX.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】有一单发票申请【%s】需要您确认税收分类编码。", impSalesContract.getBuyerName(), invoice.getDocNo()));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }

//        if(Objects.equals(invoiceStatusEnum,statusEnum)) {
//            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_SALE.getCode());
//            taskNoticeContentDTO.setContent(String.format("客户【%s】有一单发票申请【%s】需要您确认。", impSalesContract.getBuyerName(), invoice.getDocNo()));
//        } else if(Objects.equals(invoiceStatusEnum,InvoiceStatusEnum.WAIT_FINANCE_TAX)) {
//            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_TAX.getCode());
//            taskNoticeContentDTO.setContent(String.format("客户【%s】有一单发票申请【%s】需要您确认税收分类编码。", impSalesContract.getBuyerName(), invoice.getDocNo()));
//        }
//        taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
//        taskNoticeContentService.add(taskNoticeContentDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Long id) {
        Invoice invoice = super.getById(id);
        Optional.ofNullable(invoice).orElseThrow(()->new ServiceException("发票信息不存在"));
        //校验是否允许操作
        DomainStatus.getInstance().check(DomainOprationEnum.INVOICE_DELETE,invoice.getStatusId());
        //修改订单上的发票号
        impOrderService.setInvoiceNo(invoice.getOrderId(),null,"");
        //修改销售合同上的发票号
        if(StringUtils.isNotEmpty(invoice.getSaleContractNo())) {
            impSalesContractService.setInvoiceNo(null,invoice.getSaleContractNo(),"");
        }
        //删除费用明细
        invoiceCostMemberService.deleteByDocId(invoice.getId().toString());
        //删除发票明细
        invoiceMemberService.deleteByInvoiceId(invoice.getId());
        //删除历史状态
        statusHistoryService.removeHistory(invoice.getId().toString(), Invoice.class.getName());
        //删除发票主单
        super.removeById(id);
        //清空任务通知
        clearTaskNotice(id);
    }

    //清空任务通知
    public void clearTaskNotice(Long id){
        taskNoticeContentService.deleteTaskNotice(DomainTypeEnum.INVOICE.getCode(), id, "");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void completeInvoice(InvoiceCompleteDTO dto) {
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setDocumentId(dto.getId());
        taskDTO.setDocumentClass(DomainTypeEnum.INVOICE.getCode());
        taskDTO.setOperation(DomainOprationEnum.INVOICE_CW_COMPLETE.getCode());
        InvoiceStatusEnum newStatus = InvoiceStatusEnum.of(dto.getStatusId());
        taskDTO.setNewStatusCode(newStatus.getCode());
        Invoice invoice = commonService.changeDocumentStatus(taskDTO);
        //开票完成
        if(Objects.equals(InvoiceStatusEnum.INVOICED,newStatus)){
            TsfPreconditions.checkArgument(StringUtils.isNotEmpty(dto.getInvoiceNo()),new ServiceException("请填写发票号"));
            invoice.setInvoiceNo(StringUtils.null2EmptyWithTrim(dto.getInvoiceNo().replace("，",",")));
            invoice.setInvoiceDate(LocalDateTime.now());
            super.updateById(invoice);

            List<InvoiceMemberVO> invoiceMemberVOS = invoiceMemberService.getMembersByInvoiceId(invoice.getId());
            List<TaxCodeName> codeNameList = Lists.newArrayList();
            invoiceMemberVOS.stream().forEach(member ->{
                TaxCodeName codeName = new TaxCodeName();
                codeName.setId(snowflake.nextId());
                codeName.setCustomerId(invoice.getBuyerId());
                codeName.setCustomerName(invoice.getBuyerName());
                codeName.setName(member.getGoodsName());
                codeName.setInvoiceName(member.getInvoiceName());
                codeName.setCode(member.getTaxCode());
                codeName.setCodeName(member.getTaxCodeName());
                codeNameList.add(codeName);
            });
            taxCodeNameService.batchInsert(codeNameList);
        }else{
            //退回商务
            //发送任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
            taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
            taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_SALE.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】的发票申请【%s】被【%s】退回，需要您重新确认。",invoice.getBuyerName(),invoice.getDocNo(),SecurityUtil.getCurrentPersonName()));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void invoiceApply(Long id) {
        String lockKey = DistributedLockEnum.IMP_ORDER_GENERATE_INVOICE.getCode() +  ":" + id;
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            isLock = lock.tryLock(lockTime, TimeUnit.SECONDS);
            if (!isLock) {
                log.error(String.format("根据订单申请发票未获取到锁，销售合同id：【%s】", id));
                throw new ServiceException("服务拥挤，请稍后再试");
            }
            List<ImpOrderInvoiceVO> impOrderInvoiceVOS = impOrderInvoiceService.idsList(Arrays.asList(id.toString()));
            TsfPreconditions.checkArgument(CollUtil.isNotEmpty(impOrderInvoiceVOS),new ServiceException("未查询到订单对应发票信息"));
            ImpOrderInvoiceVO impOrderInvoiceVO = impOrderInvoiceVOS.get(0);
            BusinessTypeEnum businessTypeEnum = BusinessTypeEnum.of(impOrderInvoiceVO.getBusinessType());
            TsfPreconditions.checkArgument(StringUtils.isNotEmpty(impOrderInvoiceVO.getInvoiceBankName()) && StringUtils.isNotEmpty(impOrderInvoiceVO.getInvoiceBankAccount())
                    && StringUtils.isNotEmpty(impOrderInvoiceVO.getTaxpayerNo()),new ServiceException("客户开票银行信息还未完善，请先完善"));
            TsfPreconditions.checkArgument(Objects.equals(Boolean.TRUE,impOrderInvoiceVO.getIsImp()),new ServiceException("确认进口的订单才能操作"));
            TsfPreconditions.checkArgument(Objects.isNull(impOrderInvoiceVO.getInvId()),new ServiceException("已经生成发票申请，请不要重复申请！"));
            switch (businessTypeEnum){
                case IMP_COOPERATE://根据销售合同创建
                    TsfPreconditions.checkArgument(Objects.nonNull(impOrderInvoiceVO.getScId()),new ServiceException("自营业务，请先创建销售合同！"));
                    ImpSalesContract impSalesContract = impSalesContractService.getById(impOrderInvoiceVO.getScId());
                    Optional.ofNullable(impSalesContract).orElseThrow(()->new ServiceException("销售合同信息不存在或者已经被删除"));
                    //获取销售合同明细信息
                    List<ImpSalesContractMemberVO> members = impSalesContractMemberService.getMembersBySalesContractId(impOrderInvoiceVO.getScId());
                    //生成开票
                    generateInvoiceFromSalesContract(impSalesContract,members);
                    break;
                case IMP_AGENT://根据订单创建
                    //校验订单是否存在费用可以开票
                    List<ImpOrderCost> orderCosts = impOrderCostService.getAgentOrderCosts(impOrderInvoiceVO.getId());
                    BigDecimal invoiceVal = CollUtil.isEmpty(orderCosts) ? BigDecimal.ZERO : StringUtils.rounded2(orderCosts.stream().map(ImpOrderCost::getReceAmount).reduce(BigDecimal.ZERO,BigDecimal::add));
                    log.info("订单【{}】可开票金额为【{}】",impOrderInvoiceVO.getDocNo(),invoiceVal);
                    TsfPreconditions.checkArgument(invoiceVal.compareTo(BigDecimal.ZERO) == 1,new ServiceException(String.format("订单【%s】不存在可开票费用",impOrderInvoiceVO.getDocNo())));
                    //创建发票信息
                    generateInvoice(impOrderInvoiceVO,orderCosts);
                    break;
            }

        } catch (InterruptedException e) {
            log.error(String.format("根据订单申请发票获取锁异，报关单id：【%s】", id),e);
            throw new ServiceException("服务拥挤，请稍后再试");
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void financialTaxCode(ConfirmTaxCodeDTO dto) {
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setDocumentId(dto.getId());
        taskDTO.setDocumentClass(DomainTypeEnum.INVOICE.getCode());
        taskDTO.setOperation(DomainOprationEnum.INVOICE_CONFIRM_TAX.getCode());
        StatusHistoryVO statusHistoryVO = statusHistoryService.findRecentByDomainAndNowStatus(dto.getId().toString(),Invoice.class.getName(),InvoiceStatusEnum.WAIT_FINANCE_TAX.getCode());
        if(Objects.nonNull(statusHistoryVO)&&statusHistoryVO.getOriginalStatusId().equals(InvoiceStatusEnum.WAIT_CONFIRM.getCode())){
            //待商务确定开票
            taskDTO.setNewStatusCode(InvoiceStatusEnum.WAIT_CONFIRM.getCode());
        }else{
            //待客户确定编码
            taskDTO.setNewStatusCode(InvoiceStatusEnum.WAIT_CUSTOMER_TAX.getCode());
        }
        Invoice invoice = commonService.changeDocumentStatus(taskDTO);
        //处理明细
        Customer customer = new Customer();
        customer.setId(Long.valueOf(0));
        customer.setName("默认数据");
        invoiceMemberService.saveMemberTaxCode(dto.getMembers(),customer);
        if(Objects.equals(InvoiceStatusEnum.WAIT_CONFIRM,InvoiceStatusEnum.of(invoice.getStatusId()))){
            //发送任务通知(给商务)
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
            taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
            taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_SALE.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】的发票申请【%s】财务已经确认税收编码，请您尽快确认。",invoice.getBuyerName(),invoice.getDocNo()));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void businessConfirm(TaskDTO dto) {
        Invoice invoice = commonService.changeDocumentStatus(dto);
        Agreement agreement = agreementService.obtainCustomerNewEffective(invoice.getBuyerId());
        Optional.ofNullable(agreement).orElseThrow(()->new ServiceException("未找到客户有效的协议报价"));
        TsfPreconditions.checkArgument(Objects.equals(agreement.getIsSignBack(),Boolean.TRUE),new ServiceException("客户协议报价正本还未签回"));
        if(Objects.equals(InvoiceStatusEnum.WAIT_FINANCE_TAX,InvoiceStatusEnum.of(invoice.getStatusId()))){
            //发送任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
            taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
            taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_TAX.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】的发票申请【%s】需要您调整税收分类编码：%s",invoice.getBuyerName(),invoice.getDocNo(),StringUtils.removeSpecialSymbol(dto.getMemo())));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);
        }else{
            //发送任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
            taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
            taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CW_COMPLETE.getCode());
            taskNoticeContentDTO.setContent(String.format("客户【%s】的发票申请【%s】商务已提交开票确认，请您尽快开出。",invoice.getBuyerName(),invoice.getDocNo(),StringUtils.removeSpecialSymbol(dto.getMemo())));
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
            taskNoticeContentService.add(taskNoticeContentDTO);

            //修改确认日期
            invoice.setConfirmDate(LocalDateTime.now());
            super.updateById(invoice);
        }
    }

    @Override
    public PageInfo<ClientInvoiceListVO> clientPageList(ClientInvoiceQTO qto) {
        qto.setCustomerId(SecurityUtil.getCurrentCustomerId());
        PageHelper.startPage(qto.getPage(),qto.getLimit());
        if(StringUtils.isNotEmpty(qto.getClientStatusId()) && ClientStatusMappingUtil.INVOICE_CLENT_STATUS_MAPPING.containsKey(qto.getClientStatusId())) {
            Map<String, ClientInfoStatus> clientStatusMapping = ClientStatusMappingUtil.INVOICE_CLENT_STATUS_MAPPING;
            qto.setStatusIds(clientStatusMapping.get(qto.getClientStatusId()).getBackStatusId());
        }
        List<ClientInvoiceListVO> dataList = invoiceMapper.clientPageList(qto);
        return new PageInfo<>(dataList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clientConfrim(ClientConfirmInvoiceDTO dto) {
        String lockKey = DistributedLockEnum.CUSTOMER_CONFIRM_TAX.getCode() +  ":" + dto.getId();
        Lock lock = redisLockRegistry.obtain(lockKey);
        boolean isLock;
        try {
            isLock = lock.tryLock(lockTime, TimeUnit.SECONDS);
            if (!isLock) {
                log.error(String.format("客户确定税收编码未获取到锁，发票申请id：【%s】", dto.getId()));
                throw new ServiceException("服务拥挤，请稍后再试");
            }
            TaskDTO taskDTO = new TaskDTO();
            taskDTO.setDocumentId(dto.getId());
            taskDTO.setDocumentClass(DomainTypeEnum.INVOICE.getCode());
            taskDTO.setNewStatusCode(InvoiceStatusEnum.WAIT_CONFIRM.getCode());
            taskDTO.setMemo(dto.getReason());
            taskDTO.setOperation(DomainOprationEnum.INVOICE_CONFIRM_CUSTOMER.getCode());
            Invoice invoice = commonService.changeDocumentStatus(taskDTO);
            //发送任务通知
            TaskNoticeContentDTO taskNoticeContentDTO = new TaskNoticeContentDTO();
            taskNoticeContentDTO.setDocumentType(DomainTypeEnum.INVOICE.getCode());
            taskNoticeContentDTO.setDocumentId(invoice.getId().toString());
            taskNoticeContentDTO.setCustomerId(invoice.getBuyerId());
            taskNoticeContentDTO.setOperationCode(DomainOprationEnum.INVOICE_CONFIRM_SALE.getCode());
            taskNoticeContentDTO.setQueryParamsMap(ImmutableMap.of("docNo",invoice.getDocNo()));
            if(dto.getConfirm()){
                // 客户同意
                taskNoticeContentDTO.setContent(String.format("客户【%s】的发票申请【%s】税收编码已同意：%s。",invoice.getBuyerName(),invoice.getDocNo(),StringUtils.removeSpecialSymbol(dto.getReason())));
            }else{
                // 客户不同意
                taskNoticeContentDTO.setContent(String.format("客户【%s】的发票申请【%s】税收编码不同意：%s。",invoice.getBuyerName(),invoice.getDocNo(),StringUtils.removeSpecialSymbol(dto.getReason())));
            }
            taskNoticeContentService.add(taskNoticeContentDTO);

        } catch (InterruptedException e) {
            log.error(String.format("客户确定税收编码获取到锁异常，发票申请id：【%s】", dto.getId()));
            throw new ServiceException("服务拥挤，请稍后再试");
        }finally {
            //释放锁
            lock.unlock();
        }
    }

    @Override
    public ImpOrderInvoiceVO getInvoiceByOrderId(Long orderId) {
        List<ImpOrderInvoiceVO> impOrderInvoiceVOS = impOrderInvoiceService.idsList(Arrays.asList(orderId.toString()));
        TsfPreconditions.checkArgument(CollUtil.isNotEmpty(impOrderInvoiceVOS),new ServiceException("未查询到订单对应发票信息"));
        ImpOrderInvoiceVO impOrderInvoiceVO = impOrderInvoiceVOS.get(0);
        TsfPreconditions.checkArgument(Objects.nonNull(impOrderInvoiceVO.getInvId()),new ServiceException("还未生成发票申请，请耐心等待！"));
        return impOrderInvoiceVO;
    }
}
